package com.zhangfb95.crosschain.server.service.impl;

import com.zhangfb95.crosschain.infra.exception.BusinessException;
import com.zhangfb95.crosschain.protocol.exc.request.GetBlockByNumberExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.GetHeightExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.GetTxByIdExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.QueryExcReq;
import com.zhangfb95.crosschain.protocol.exc.request.SendExcReq;
import com.zhangfb95.crosschain.protocol.exc.response.GetBlockByNumberExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.GetHeightExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.GetTxByIdExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.QueryExcRes;
import com.zhangfb95.crosschain.protocol.exc.response.SendExcRes;
import com.zhangfb95.crosschain.protocol.routerpluginapi.PluginService;
import com.zhangfb95.crosschain.protocol.routerpluginapi.PluginServiceRegistry;
import com.zhangfb95.crosschain.protocol.routerpluginapi.request.QueryPluginReq;
import com.zhangfb95.crosschain.protocol.routerpluginapi.request.SendPluginReq;
import com.zhangfb95.crosschain.protocol.routerpluginapi.response.GetBlockByNumberPluginRes;
import com.zhangfb95.crosschain.protocol.routerpluginapi.response.GetTxByIdPluginRes;
import com.zhangfb95.crosschain.protocol.routerpluginapi.response.QueryPluginRes;
import com.zhangfb95.crosschain.protocol.routerpluginapi.response.SendPluginRes;
import com.zhangfb95.crosschain.server.service.ChainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zhangfubing
 * @since 2023/12/4
 */
@Slf4j
@Service
public class ChainServiceImpl implements PluginServiceRegistry, ChainService {

    private final List<PluginService> pluginServices = new ArrayList<>();

    @Override
    public void register(PluginService pluginService) {
        if (pluginService.isEnable()) {
            pluginServices.add(pluginService);
        }
    }

    @Override
    public List<String> listChainName() {
        if (CollectionUtils.isEmpty(pluginServices)) {
            return Collections.emptyList();
        }
        return pluginServices.stream().map(PluginService::getChainName).collect(Collectors.toList());
    }

    @Override
    public List<String> listChainPath() {
        List<String> chainPaths = new ArrayList<>();
        pluginServices.forEach(pluginService -> {
            chainPaths.addAll(pluginService.listChainPath());
        });
        return chainPaths;
    }

    @Override
    public GetHeightExcRes getHeight(GetHeightExcReq req) {
        for (PluginService pluginService : pluginServices) {
            if (pluginService.listChainPath().contains(req.getChainPath())) {
                long height = pluginService.getHeight(req.getChainPath());
                return new GetHeightExcRes().setHeight(height);
            }
        }
        throw new BusinessException("do not find chainPath");
    }

    @Override
    public GetBlockByNumberExcRes getBlockByNumber(GetBlockByNumberExcReq req) {
        for (PluginService pluginService : pluginServices) {
            if (pluginService.listChainPath().contains(req.getChainPath())) {
                GetBlockByNumberPluginRes pluginRes =
                        pluginService.getBlockByNumber(req.getChainPath(), req.getBlockNumber());
                return new GetBlockByNumberExcRes()
                        .setBlockNumber(pluginRes.getBlockNumber())
                        .setDataHash(pluginRes.getDataHash())
                        .setBlockHash(pluginRes.getBlockHash())
                        .setPreviousHash(pluginRes.getPreviousHash())
                        .setBlockBytes(pluginRes.getBlockBytes())
                        .setTxIds(pluginRes.getTxIds());
            }
        }
        throw new BusinessException("do not find chainPath");
    }

    @Override
    public GetTxByIdExcRes getTxById(GetTxByIdExcReq req) {
        for (PluginService pluginService : pluginServices) {
            if (pluginService.listChainPath().contains(req.getChainPath())) {
                GetTxByIdPluginRes pluginRes = pluginService.getTxById(req.getChainPath(), req.getTxId());
                return new GetTxByIdExcRes()
                        .setTxId(pluginRes.getTxId())
                        .setTransactionBytes(pluginRes.getTransactionBytes());
            }
        }
        throw new BusinessException("do not find chainPath");
    }

    @Override
    public QueryExcRes query(QueryExcReq req) {
        for (PluginService pluginService : pluginServices) {
            if (pluginService.listChainPath().contains(req.getChainPath())) {
                QueryPluginReq pluginReq = new QueryPluginReq()
                        .setServerId(req.getServerId())
                        .setUserId(req.getUserId())
                        .setChainPath(req.getChainPath())
                        .setMethod(req.getMethod())
                        .setArgs(req.getArgs());
                QueryPluginRes pluginRes = pluginService.query(pluginReq);
                return new QueryExcRes().setResult(pluginRes.getResult());
            }
        }
        throw new BusinessException("do not find chainPath");
    }

    @Override
    public SendExcRes send(SendExcReq req) {
        for (PluginService pluginService : pluginServices) {
            if (pluginService.listChainPath().contains(req.getChainPath())) {
                SendPluginReq pluginReq = new SendPluginReq()
                        .setServerId(req.getServerId())
                        .setUserId(req.getUserId())
                        .setChainPath(req.getChainPath())
                        .setMethod(req.getMethod())
                        .setArgs(req.getArgs());
                SendPluginRes pluginRes = pluginService.send(pluginReq);
                return new SendExcRes()
                        .setTransactionID(pluginRes.getTransactionID())
                        .setResult(pluginRes.getResult());
            }
        }
        throw new BusinessException("do not find chainPath");
    }
}
